/*
 * Copyright (c) 2010, Nikolaus Moll
 * All rights reserved.
 */
package net.miblounge.gradecalculator.core.descriptors.modulelistitems;

import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlElements;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.bind.annotation.XmlType;

import net.miblounge.gradecalculator.core.descriptors.courses.Course;
import net.miblounge.gradecalculator.core.descriptors.courses.IndividualCourse;
import net.miblounge.gradecalculator.core.descriptors.modulelists.ModuleList;
import net.miblounge.gradecalculator.core.interfaces.IModuleIdentificator;
import net.miblounge.gradecalculator.core.interfaces.IModuleListItem;

@XmlRootElement(namespace = "http://gradecalculator.net/")
@XmlType( 
 propOrder = { "name", "moduleType", "sortOrder", "courses" } 
) 
public class Module implements IModuleListItem, Cloneable, IModuleIdentificator {
	private List<Course> courses;
	private float ects = -1;
	private int hoursperweek = -1;
	private String name;
	private int sortOrder;
	private boolean isGraded = false;
	
	private IModuleIdentificator parent;

	public Module()
	{
		this("");
	}
	
	public Module(final String name)
	{
		this.name = name;
		this.courses = new ArrayList<Course>();
	}

	@XmlTransient
	public IModuleIdentificator getParent()
	{
		return parent;
	}
	public void setParent(final IModuleIdentificator parent)
	{
		this.parent = parent;
	}
	
	@Override
	@XmlTransient	
	public float getECTS() {
		checkData();
		return ects;
	}

	@Override
	@XmlTransient	
	public int getHoursPerWeek() {
		checkData();
		return hoursperweek;
	}

	@Override
	@XmlElement(name = "type") 
	public ModuleType getModuleType() {
		return ModuleType.Module;
	}

	@Override
	@XmlElement(name = "name") 
	public String getName() {
		return name;
	}
	public void setName(final String name) {
		this.name = name;
	}

	@XmlElement(name = "sortorder") 
	public int getSortOrder() {
		return sortOrder;
	}
	public void setSortOrder(final int sortOrder) {
		this.sortOrder = sortOrder;
	}
	
	@XmlElementWrapper(name = "courses") 
	@XmlElements({
           @XmlElement(name="course",type=Course.class),
           @XmlElement(name="individualcourse",type=IndividualCourse.class)
       })
	public List<Course> getCourses()
	{
		return courses;
	}

	public void addCourse(final Course course)
	{
		// TODO: Count ects and number of graded courses instead...
		invalidate();
		if (!courses.contains(course)) {
			courses.add(course);
		}
		course.setParent(this);
	}
	
	public void removeCourse(final Course course)
	{
		ects = -1;
		courses.remove(course);
	}

	@Override
	public int compareTo(final IModuleListItem o) {
		return sortOrder - o.getSortOrder();
	}
	
	@Override
	public Module clone()
	{
		try {
			final Module result = (Module)super.clone();
			result.courses = courses;	// think about deep copy
			result.ects = ects;
			result.name = new String(name);
			result.sortOrder = sortOrder;
			return result;
		} catch (final CloneNotSupportedException e) {
			throw new AssertionError();
		}
	}
	
	@Override
	public String getIdentifier() {
		if (parent != null) {
			return parent.getIdentifier() + "." + name;
		}
		return name;
	}

	private void checkData()
	{
		if (ects > 0) {
			return;
		}
		
		ects = 0;
		hoursperweek = 0;
		isGraded = false;
		for (final Course course : courses) {
			ects += course.getECTS();
			hoursperweek += course.getHoursPerWeek();
			if (!course.getGradedPerformance().isEmpty()){
				isGraded = true;
			}
		}
	}
	
	public void invalidate()
	{
		ects = 0;
		if (parent != null) {
			((ModuleList)parent).invalidate();
		}
	}
	
	@XmlTransient
	public boolean isGraded()
	{
		checkData();
		return isGraded;
	}
	
	@Override
	public int hashCode()
	{
		checkData();
		
		int result = 17;
		result = 31 * result + name.hashCode();
		result = 31 * result + courses.hashCode();
		result = 31 * result + Float.floatToIntBits(ects);

		return result;
	}
	
	@Override
	public String toString()
	{
		return getName();
	}

	public void insertCourse(final Course course, final Course courseBefore) {
		final int index = courses.indexOf(courseBefore);
		invalidate();
		if (!courses.contains(course)) {
			courses.add(index, course);
		}
		course.setParent(this);
	}

}
